#ifndef _AtomicWrapper_H__
#define _AtomicWrapper_H__

#include <signal.h>
#include "U2PreRequest.h"
#include "U2Exception.h"



U2EG_NAMESPACE_BEGIN


template <class T> class U2AtomicObject
{
public:
    U2AtomicObject (const T &initial)
        : mField(initial)
    {   }

    U2AtomicObject (const U2AtomicObject<T> &cousin)
        : mField(cousin.get())
    {   }

    U2AtomicObject ()
    {   }

    void operator= (const U2AtomicObject<T> &cousin)
    {
        set(cousin.get());
    }

    T get (void) const
    {
        U2_LOCK_AUTO_MUTEX
        return mField;
    }

    void set (const T &v)
    {
        U2_LOCK_AUTO_MUTEX
        mField = v;
    }

    bool cas (const T &old, const T &nu)
    {
        U2_LOCK_AUTO_MUTEX
        if (mField != old) return false;
        mField = nu;
        return true;
    }

    T operator++ (void)
    {
        U2_LOCK_AUTO_MUTEX
        return ++mField;
    }

    T operator++ (int)
    {
        U2_LOCK_AUTO_MUTEX
        return mField++;
    }

    T operator-- (int)
    {
        U2_LOCK_AUTO_MUTEX
        return mField--;
    }
    
    T operator+=(const T &add)
    {
        U2_LOCK_AUTO_MUTEX
        mField += add;
        return mField;
    }

protected:
    U2_AUTO_MUTEX
    volatile T mField;
};



#if U2_COMPILER == U2_COMPILER_GNUC && U2_COMP_VER >= 412 && U2_THREAD_SUPPORT && U2_CPU != U2_CPU_ARM



template<class T> class U2AtomicScalar
{
public:
    U2AtomicScalar (const T &initial)
        : mField(initial)
    {   }

    U2AtomicScalar (const U2AtomicScalar<T> &cousin)
        : mField(cousin.mField)
    {   }

    U2AtomicScalar () 
    {   }

    void operator= (const U2AtomicScalar<T> &cousin)
    {
        mField = cousin.mField;
    }

    T get (void) const
    {
        return mField;
    }

    void set (const T &v)
    {
        mField = v; 
    }   

    bool cas (const T &old, const T &nu)
    {
        return __sync_bool_compare_and_swap (&mField, old, nu);
    }

    T operator++ (void)
    {
        __sync_add_and_fetch (&mField, 1);
    }

    T operator-- (void)
    {
        __sync_add_and_fetch (&mField, -1);
    }

    T operator++ (int)
    {
        __sync_fetch_and_add (&mField, 1);
    }

    T operator-- (int)
    {
        __sync_fetch_and_add (&mField, -1);
    }

    T operator+=(const T &add)
    {
        __sync_fetch_and_add (&mField, add);
    }


    volatile T mField;
};



#elif U2_COMPILER == U2_COMPILER_MSVC && U2_COMP_VER >= 1400 && U2_THREAD_SUPPORT

#ifndef WIN32_LEAN_AND_MEAN
#   define WIN32_LEAN_AND_MEAN
#endif
#if !defined(NOMINMAX) && defined(_MSC_VER)
#	define NOMINMAX // required to stop windows.h messing up std::min
#endif
#include <windows.h>
#include <intrin.h>



template<class T> class U2AtomicScalar
{
public:
    U2AtomicScalar (const T &initial)
        : mField(initial)
    {   }

    U2AtomicScalar (const U2AtomicScalar<T> &cousin)
        : mField(cousin.mField)
    {   }

    U2AtomicScalar () 
    {   }

    void operator= (const U2AtomicScalar<T> &cousin)
    {
        mField = cousin.mField;
    }

    T get (void) const
    {
        return mField;
    }

    void set (const T &v)
    {
        mField = v;
    }   

    bool cas (const T &old, const T &nu)
    {
        if (sizeof(T)==2) {
            return _InterlockedCompareExchange16((SHORT*)&mField, static_cast<SHORT>(nu), static_cast<SHORT>(old)) == static_cast<SHORT>(old);
        } 
        else if (sizeof(T)==4) 
        {
            return _InterlockedCompareExchange((LONG*)&mField, static_cast<LONG>(nu), static_cast<LONG>(old)) == static_cast<LONG>(old);
        } 
        else if (sizeof(T)==8) {
            return _InterlockedCompareExchange64((LONGLONG*)&mField, static_cast<LONGLONG>(nu), static_cast<LONGLONG>(old)) == static_cast<LONGLONG>(old);
        } 
        else {
            U2_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,"Only 16, 32, and 64 bit scalars supported in win32.","AtomicScalar::cas");
        }
    }

    T operator++ (void)
    {
        if (sizeof(T)==2) {
            return InterlockedIncrement16((SHORT*)&mField);
        } else if (sizeof(T)==4) {
            return InterlockedIncrement((LONG*)&mField);
        } else if (sizeof(T)==8) {
            return InterlockedIncrement64((LONGLONG*)&mField);
        } else {
            U2_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,"Only 16, 32, and 64 bit scalars supported in win32.","AtomicScalar::operator++(prefix)");
        }
    }

    T operator-- (void)
    {
        if (sizeof(T)==2) {
            return InterlockedDecrement16((SHORT*)&mField);
        } else if (sizeof(T)==4) {
            return InterlockedDecrement((LONG*)&mField);
        } else if (sizeof(T)==8) {
            return InterlockedDecrement64((LONGLONG*)&mField);
        } else {
            U2_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,"Only 16, 32, and 64 bit scalars supported in win32.","AtomicScalar::operator--(prefix)");
        }
    }

    T operator++ (int)
    {
        if (sizeof(T)==2) {
            return InterlockedIncrement16((SHORT*)&mField)-1;
        } else if (sizeof(T)==4) {
            return InterlockedIncrement((LONG*)&mField)-1;
        } else if (sizeof(T)==8) {
            return InterlockedIncrement64((LONGLONG*)&mField)-1;
        } else {
            U2_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,"Only 16, 32, and 64 bit scalars supported in win32.","AtomicScalar::operator++(postfix)");
        }
    }

    T operator-- (int)
    {
        if (sizeof(T)==2) {
            return InterlockedDecrement16((SHORT*)&mField)+1;
        } else if (sizeof(T)==4) {
            return InterlockedDecrement((LONG*)&mField)+1;
        } else if (sizeof(T)==8) {
            return InterlockedDecrement64((LONGLONG*)&mField)+1;
        } else {
            U2_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,"Only 16, 32, and 64 bit scalars supported in win32.","AtomicScalar::operator--(postfix)");
        }
    }

    T operator+=(const T &add)
    {
        //The function InterlockedExchangeAdd is not available for 64 and 16 bit version
        //We will use the cas operation instead. 
        T newVal;
        do {
            //Create a value of the current field plus the added value
            newVal = mField + add;
            //Replace the current field value with the new value. Ensure that the value 
            //of the field hasn't changed in the mean time by comparing it to the new value
            //minus the added value. 
        } while (!cas(newVal - add, newVal)); //repeat until successful
        return newVal;
    }

    volatile T mField;
};


#else


template <class T> class U2AtomicScalar
{
public:
    U2AtomicScalar (const T &initial)
        : mField(initial)
    {   }

    U2AtomicScalar (const U2AtomicScalar<T> &cousin)
        : mField(cousin.mField)
    {   }

    U2AtomicScalar ()
    {   }

    void operator= (const U2AtomicScalar<T> &cousin)
    {
        U2_LOCK_AUTO_MUTEX
        mField = cousin.mField;
    }

    T get (void) const
    {
        // no lock required here
        // since get will not interfere with set or cas
        // we may get a stale value, but this is ok
        return mField;
    }

    void set (const T &v)
    {
        U2_LOCK_AUTO_MUTEX
        mField = v;
    }

    bool cas (const T &old, const T &nu)
    {
        U2_LOCK_AUTO_MUTEX
        if (mField != old) return false;
        mField = nu;
        return true;
    }

    T operator++ (void)
    {
        U2_LOCK_AUTO_MUTEX
        return ++mField;
    }

    T operator-- (void)
    {
        U2_LOCK_AUTO_MUTEX
        return --mField;
    }

    T operator++ (int)
    {
        U2_LOCK_AUTO_MUTEX
        return mField++;
    }

    T operator-- (int)
    {
        U2_LOCK_AUTO_MUTEX
        return mField--;
    }

    T operator+=(const T &add)
    {
        U2_LOCK_AUTO_MUTEX
        mField += add;
        return mField;
    }

protected:
    U2_AUTO_MUTEX

    volatile T mField;
};



#endif


U2EG_NAMESPACE_END

#endif